package net.sh.dpinfrastructure.util;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * 
 * 循环帮助类
 * 
 * @version 1.0
 * @since JDK1.7
 * @author shaohui
 * @company 上海朝阳永续信息技术有限公司
 * @copyright (c) 2017 SunTime Co'Ltd Inc. All rights reserved.
 * @date 2017年1月17日 下午4:36:09
 */
public class DPLoopUtils {

    /**
     * 
     *  
     * while循环的帮助类，可以响应线程中断信号主要是规避不正确的循环（如死循环）<br>
     * 可接受线程中断信号的while循环.如果外部已经调用过 {@link Thread#interrupted()}方法且在调用该方法执行前并未再调用过 {@link Thread#interrupt()} 则该循环不能被中断
     *
     * @param loopHandler
     * @return 最终循环次数，当遇到死循环（或循环次数过于巨大时）该数字可能不准确
     * @throws InterruptedException 被中断时抛出
     * 
     * @author shaohui
     * @date 2017年1月17日 下午5:36:12
     */
    public static long interruptedWhile(LoopHandler loopHandler) throws InterruptedException {
        long i = 0;
        Thread t = Thread.currentThread();
        while(true) {
            i++;
            if (t.isInterrupted()) { // 判断是否有线程中断信号进来
                throw new InterruptedException("loop interrupted,total cycle is " + i + "times,this number is just for reference and may incorrect when this loop is endless or loop too many times");
            }
            if (!loopHandler.onLoop(i)) {
                break;
            }
        }
        return i;
    }
    
    /**
     * 
     * 限时的while循环<br>
     * 循环将在指定的超时时间内没有完成时结束循环并抛出 {@link TimeoutException}异常
     *
     * @param loopHandler 循环处理类，不能为空
     * @param timeout 超时时限，必须大于0
     * @param es 可以为空，当为空时将新建一个Executor并在任务完成后（或发生异常时）关闭
     * @return
     * 
     * @author shaohui
     * @throws ExecutionException 循环内部发生异常抛出
     * @throws InterruptedException 循环操作被打断抛出
     * @throws TimeoutException 超时抛出
     * @throws NullPointerException loopHander 为空时抛出
     * @throws IllegalArgumentException timeout 不大于0时抛出
     * @date 2017年1月18日 上午9:13:13
     */
    public static long timeLimitedWhile(final LoopHandler loopHandler, long timeout, ExecutorService es) throws InterruptedException, ExecutionException, TimeoutException{
        if (loopHandler == null) {
            throw new NullPointerException("loop hander");
        }
        if (timeout <= 0) {
            throw new IllegalArgumentException("timeout less than 0 ");
        }
        boolean needToShutDownExecutor = false;
        if (es == null) {
            needToShutDownExecutor = true;
            es = Executors.newSingleThreadExecutor();
        }
        FutureTask<Long> f = new FutureTask<>(new Callable<Long>() {

            @Override
            public Long call() throws Exception {
                return interruptedWhile(loopHandler);
            }
            
        });
        es.execute(f);
        try {
            return f.get(timeout, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            f.cancel(true);
            throw e;
        } catch (ExecutionException e) {
            f.cancel(true);
            throw e;
        } catch (TimeoutException e) {
            f.cancel(true);
            throw e;
        } finally {
            if (needToShutDownExecutor) {
                es.shutdown();
            }
        }
    }
    
    /**
     * 
     * 可中断的for循环<br>
     * 按照如下的原则循环：
     * <pre>
     *  for (i = from; i < to; i += step) {
     *      // do loop
     *  }
     * </pre>
     * 因此要合理设置开始和技术下标以及步进，比如，当 step 大于 0 时，from 应 小于 to；小于 0 时，from 应大于 to；step 尽量避免为 0.当违反上述原则时，则可能出现不可预料的情况（比如死循环或循环一次都不执行）
     *
     * @param loopHandler
     * @param from 开始下标（包括）
     * @param to 结束下标（不包括）
     * @param step 步进
     * @return 最后一次循环所在的索引
     * @throws InterruptedException
     * 
     * @author shaohui
     * @date 2017年1月17日 下午4:41:52
     */
    public static long interruptedFor(LoopHandler loopHandler, long from, long to, long step) throws InterruptedException {
        Thread t = Thread.currentThread();
        long index = from;
        for (; index < to; index += step) {
            if (t.isInterrupted()) { // 判断是否有线程中断信号进来
                throw new InterruptedException("loop interrupted，the end index is " + index);
            }
            if (!loopHandler.onLoop(index)) {
                break;
            }
        }
        return index;
    }
    
    /**
     * 
     * 限时的while循环<br>
     * 循环将在指定的超时时间内没有完成时结束循环并抛出 {@link TimeoutException}异常
     *
     * @param loopHandler 循环处理类，不能为空
     * @param from 开始下标（包括）
     * @param to 结束下标（不包括）
     * @param step 步进
     * @param timeout 超时时限，必须大于0
     * @param es 可以为空，当为空时将新建一个Executor并在任务完成后（或发生异常时）关闭
     * @see {@link #interruptedFor(LoopHandler, long, long, long)}
     * @return
     * 
     * @author shaohui
     * @throws ExecutionException 循环内部发生异常抛出
     * @throws InterruptedException 循环操作被中断抛出
     * @throws TimeoutException 超时抛出
     * @throws NullPointerException loopHander 为空时抛出
     * @throws IllegalArgumentException timeout 不大于0时抛出
     * @date 2017年1月18日 上午9:13:13
     */
    public static long timeLimitedFor(LoopHandler loopHandler, long from, long to, long step, long timeout, ExecutorService es) throws InterruptedException, ExecutionException, TimeoutException {
        if (loopHandler == null) {
            throw new NullPointerException("loopHander");
        }
        if (timeout <= 0) {
            throw new IllegalArgumentException("timeout less than 0 ");
        }
        boolean needToShutDownExecutor = false;
        if (es == null) {
            needToShutDownExecutor = true;
            es = Executors.newSingleThreadExecutor();
        }
        FutureTask<Long> f = new FutureTask<>(new Callable<Long>() {

            @Override
            public Long call() throws Exception {
                return interruptedFor(loopHandler, from, to, step);
            }
            
        });
        es.execute(f);
        try {
            return f.get(timeout, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            f.cancel(true);
            throw e;
        } catch (ExecutionException e) {
            f.cancel(true);
            throw e;
        } catch (TimeoutException e) {
            f.cancel(true);
            throw e;
        } finally {
            if (needToShutDownExecutor) {
                es.shutdown();
            }
        }
    }
    
    /**
     * 
     * 循环处理类
     * 
     * @version 1.0
     * @since JDK1.7
     * @author shaohui
     * @company 上海朝阳永续信息技术有限公司
     * @copyright (c) 2017 SunTime Co'Ltd Inc. All rights reserved.
     * @date 2017年1月17日 下午4:44:30
     */
    public interface LoopHandler {
        
        /**
         * 
         * 处理循环
         *
         * @param currentIndex 在 while 循环中表示当前循环次数，在 for 循环中表示当前索引
         * @return 是否继续循环标识，true：继续循环，false：跳出循环
         * 
         * 
         * @author shaohui
         * @date 2017年1月17日 下午4:44:35
         */
        boolean onLoop(long currentIndex);
    }
}
